#ifndef _CORE_H_
#define _CORE_H_

#include "device/Bus.h"
#include "core/Trap.h"
#include "core/ITrace.h"
#include "core/Register.h"
#include "core/difftest.h"
#include "core/Instruction.h"

#include <array>
#include <filesystem>

#define RESET_VECTOR (word_t)(0x80000000ul)

struct Package {
    uint32_t pc;
    Instruction inst;
    uint32_t inst_val;
    word_t opcode;
    word_t rd;
    word_t rs1;
    word_t rs2;
    word_t funct3;
    word_t funct7;
    word_t imm;
    word_t shamt;
    word_t csr;
    word_t zimm;
};

class Core {
public:
    Core(Bus &bus);
    IFDEF(CONFIG_ITRACE, ITrace* itrace);
    void step(bool msip, bool mtip);
    word_t load(word_t addr, size_t size, bool& mmio);
    void store(word_t addr, size_t size, word_t data, bool& mmio);
    const std::array<std::string, 32> gpr_name_table = {
        "zero", "ra",  "sp",  "gp",  "tp", "t0", "t1", "t2", 
        "s0",   "s1 ", "a0",  "a1",  "a2", "a3", "a4", "a5", 
        "a6",   "a7 ", "s2",  "s3",  "s4", "s5", "s6", "s7", 
        "s8",   "s9 ", "s10", "s11", "t3", "t4", "t5", "t6"
    };
#ifdef CONFIG_DIFFTEST
    DifftestContext* dut_ctx;
    DifftestContext* ref_ctx;
    bool is_skip_ref;
    void difftest_init(const char *ref_file, uint8_t *mem_base, size_t mem_size);
    void difftest_step(word_t pc);
    void difftest_skip_ref();
    void difftest_raise_intr(uint64_t NO);
    void difftest_check(word_t pc, DifftestContext* ref, DifftestContext* dut);
#endif

    Bus& bus;
    word_t pc;
    // GPR gpr;
    // CSR csr;
    std::array<word_t, 32> gpr;
    std::array<word_t, 4096> csr;
    
    void fetch(Package& pkg);
    void decode(Package& pkg);
    void execute(Package& pkg);

    void reset();

    word_t take_trap(Trap trap, word_t epc);
    inline void update_mip(bool msip, bool mtip);
    inline void check_interrupt();
    
    void open_trace(const char* log_path);
};

#endif
